#pragma once

#include <string>

namespace sca {
    /**
     * the kind of a instruction
     */
    enum class InstructionKind {
        JUMP,
        RET,
        CALL,
        OTHER
    };

    /**
     * Remove leading and trailing whitespace characters from a string
     * @param s the string that needs to remove leading and trailing whitespace characters
     */
    static void trim(std::string &s) {
        if (s.empty()) {
            return;
        }
        s.erase(0, s.find_first_not_of(' '));
        s.erase(s.find_last_not_of(' ') + 1);
    }

    /**
     * Find the nth str from s
     * @param s   the searched string
     * @param str the substring to be found
     * @param n  need to find the nth character
     * @return the index of Nth str
     */
    static std::string::size_type getNthSpecChar(std::string &s, std::string &str, int n) {
        using StrIndex = std::string::size_type;
        StrIndex beginIndex = 0;
        StrIndex strLen = str.size();
        StrIndex resIndex;
        while (n-- != 0) {
            resIndex = s.find(str, beginIndex);
            if (resIndex == std::string::npos)
                return std::string::npos;
            beginIndex = resIndex + strLen;
        }
        return resIndex;
    }

    /**
     * get the kind of the instruction
     * @param instruction
     * @return
     */
    static InstructionKind getInstructionKind(const std::string &instruction) {
        if (instruction.find('j') != std::string::npos) {
            return InstructionKind::JUMP;
        }
        if (instruction.find("ret") != std::string::npos) {
            return InstructionKind::RET;
        }
        if (instruction.find("call") != std::string::npos) {
            return InstructionKind::CALL;
        }
        return InstructionKind::OTHER;
    }

    /**
     *
     * @param instruction
     * @return the destination address of a jump instruction
     */
    static std::string getAddressFromJ(const std::string &instruction) {
        using StrIndex = std::string::size_type;
        if (instruction.find('j', 0) != std::string::npos) {
            StrIndex index = instruction.find_first_of(' ', 0);
            StrIndex addBeginIndex = instruction.find_first_not_of(' ', index);
            StrIndex addEndIndex = instruction.find_first_of(' ', addBeginIndex);
            return instruction.substr(addBeginIndex, addEndIndex - addBeginIndex);
        }
        return {};
    }

    /**
     *
     * @param instruction
     * @return the destination address of a jump instruction
     */
    static std::string getAddressFromCall(const std::string &instruction) {
        if (instruction.find("call", 0) != std::string::npos) {
            size_t index = instruction.find(' ', 0);
            size_t addBeginIndex = instruction.find_first_not_of(' ', index);
            size_t addEndIndex = instruction.find_first_of(' ', addBeginIndex);
            if(addEndIndex == std::string::npos){
                return instruction.substr(addBeginIndex);
            }
            return instruction.substr(addBeginIndex, addEndIndex - addBeginIndex);
        }
        return {};
    }

    /**
     * 去除指令字符串的注释
     * @param instruction 需要去除注释的指令字符串
     * @return 去除注释的指令字符串
     */
    static std::string removeComments(const std::string &instruction){
        // 查询注释的起始索引
        size_t indexComment = instruction.find('#');
        // 如果指令字符串没有注释，直接返回
        if(indexComment == std::string::npos)
            return instruction;

        std::string instWithNoComment =  instruction.substr(0,indexComment);
        // 去除空格
        trim(instWithNoComment);
        return instWithNoComment;
    }
}